package com.test.myrpc.client;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.*;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;

/**对于 @RpcClient 注解的扫描类 RpcInitConfig，将其注册进Spring容器中
 * 由于这里注册的是工厂类，所以我们建立一个工厂类 RpcClinetFactoryBean继承Spring中的FactoryBean类，
 * 由其统一创建@RpcClient注解的代理类
 *
 **/
public class RpcInitConfig implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider provider = getScanner();
        //设置扫描器
        provider.addIncludeFilter(new AnnotationTypeFilter(RpcClient.class));
        //扫描此包下的所有带有@RpcClient的注解的类
        Set<BeanDefinition> beanDefinitionSet = provider.findCandidateComponents("com.test.myrpc.client");
        for (BeanDefinition beanDefinition : beanDefinitionSet) {
            if (beanDefinition instanceof AnnotatedBeanDefinition) {
                //获得注解上的参数信息
                AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
                String beanClassAllName = beanDefinition.getBeanClassName(); //类名
                Map<String, Object> paraMap = annotatedBeanDefinition.getMetadata()
                        .getAnnotationAttributes(RpcClient.class.getCanonicalName());
                /** 手动创建动态代理对象bean注入到spring容器
                 * 使用@bean注解已经无法满足需求，因此我们可以改用原生的spring操作，来进行注册bean。
                 * 首先是一般的原生注册bean操作都是利用BeanDefinitionBuilder首先，只能实例化非接口的类，写了个简单的工具类，
                 * registryBean方法是一般bean注册方案        https://blog.csdn.net/qq_20597727/article/details/80996190
                 *
                 */
                //将RpcClient的工厂类注册进去, 由于上面注册的是工厂类，所以我们建立一个工厂类RpcClinetFactoryBean继承Spring中的FactoryBean类，由其统一创建@RpcClient注解的代理类
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RpcClinetFactoryBean.class);
                //设置RpcClinetFactoryBean工厂类中的构造函数的值
                builder.addConstructorArgValue(beanClassAllName);
                builder.getBeanDefinition().setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
                //将其注册进容器中(将key为接口名，value为动态接口类 注册进Spring容器中)
                registry.registerBeanDefinition(beanClassAllName, builder.getBeanDefinition());
            }
        }
    }

    //允许Spring扫描接口上的注解
    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
            }
        };
    }
}

